package com.base.web.zookeeper.service.impl;

import com.base.web.zookeeper.cache.ZkPropertis;
import com.base.web.zookeeper.model.Property;
import com.base.web.zookeeper.service.ZkService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.apache.commons.lang3.StringUtils;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @Des 通过Curator操作zookeeper的实现
 * Created by wxq on 2018/8/21.
 */
@Service
public class ZkServiceImpl implements ZkService{

    private final static Logger log = LoggerFactory.getLogger(ZkServiceImpl.class);

    private static CuratorFramework client;

    private ThreadLocal<List<String>> localPath = new ThreadLocal<List<String>>();

    /**
     * 注入时初始化client
     */
    public ZkServiceImpl(){

        RetryPolicy retryPolicy = new ExponentialBackoffRetry(ZkPropertis.RETRY_TIME, ZkPropertis.TIMES);//设定重试规则，args[0]:重试的时间间隔（单位毫秒），args[1]:重试的次数
        client = CuratorFrameworkFactory.builder()
                .connectString(ZkPropertis.ADDRESS)
                .sessionTimeoutMs(ZkPropertis.SESSION_OUT_TIME)
                .connectionTimeoutMs(ZkPropertis.CONNECTION_OUT_TIME)
                .retryPolicy(retryPolicy)
                .build();
        client.start();

    }

    @Override
    public boolean addNode(String path) {
        try {
            checkNodeExists(path);
        } catch (Exception e) {
            return true;
        }
        try {
            client.create().forPath(path);
        } catch (Exception e) {
            log.error("创建节点失败,失败原因：" + e);
            throw new RuntimeException("Failed to create node!");
        }
        return true;
    }

    @Override
    public boolean addNode(String path, Object data) {
        data = path.substring(path.indexOf("=") + 1);
        path = path.substring(0,path.indexOf("=")).replace(".","/");
        String[] args = path.split("/");
        if(args.length > 1){
            List<String> list = Arrays.asList(args);
            String rPath = "";
            for(int i = 0; i < list.size() ; i++){
                rPath += "/" + list.get(i);
                if(!(i == list.size() - 1)){
                    addNode(rPath);
                }else{
                    checkNodeExists("/" + path);
                    try {
                        client.create().forPath("/" + path,data.toString().getBytes());
                    } catch (Exception e) {
                        log.error("创建节点失败,失败原因：" + e);
                        throw new RuntimeException("Failed to create node!");
                    }
                }
            }
        }else{
            checkNodeExists(path);
            try {
                client.create().forPath(path,data.toString().getBytes());
            } catch (Exception e) {
                log.error("创建节点失败,失败原因：" + e);
                throw new RuntimeException("Failed to create node!");
            }
        }
        return true;
    }

    @Override
    public boolean updateNode(String path, Object data) {
        checkNodeNotExists(path);
        if(data == null){
            log.error("修改节点时,传入数据为空");
            throw new RuntimeException("Data must exist!");
        }else{
            try {
                client.setData().forPath(path,data.toString().getBytes());
            } catch (Exception e) {
                log.error("修改节点数据失败,失败原因：" + e);
                throw new RuntimeException("Failed to modify data!");
            }
        }
        return true;
    }

    @Override
    public boolean deleteNode(String path) {
        checkNodeNotExists(path);
        try {
            client.delete().deletingChildrenIfNeeded().forPath(path);
        } catch (Exception e) {
            log.error("删除节点失败,失败原因：" + e);
            throw new RuntimeException("Failed to delete node!");
        }
        return true;
    }

    @Override
    public String queryNodeData(String path) {
        checkNodeNotExists(path);
        try {
            return new String(client.getData().forPath(path));
        } catch (Exception e) {
            log.error("获取节点数据失败,失败原因：" + e);
            throw new RuntimeException("Failed to get node data!");
        }
    }

    @Override
    public Map<String, String> queryAllNodeAndData() {
        Map<String, String> map = new HashMap<>();
        localPath.set(new ArrayList<String>());

        String path = queryAllPath("/");

        List<String> list = localPath.get();

        if(list.size() > 0){
            for(String str : list){
                String data = null;
                try {
                    data = new String(client.getData().forPath(str));
                } catch (Exception e) {
                    continue;
                }
                if(StringUtils.isNotEmpty(data))
                    map.put(str,data);
            }
        }

        return map;
    }

    @Override
    public Map<String, String> queryChildrenNodeAndData(String path) {
        checkNodeNotExists(path);

        Map<String,String> map = new HashMap<String,String>();
        try {
            List<String> child_path_list = queryChildPathList(path);
            for(String str : child_path_list){
                checkNodeExists(str);
                String data = new String(client.getData().forPath(str));
                map.put("/" + str,data);
            }
        } catch (Exception e) {
            log.error("获取子列表失败,失败原因：" + e);
            throw new RuntimeException("Failed to get child node list!");
        }
        return map;
    }

    @Override
    public void checkNodeExists(String path) {
        Stat stat = null;
        try {
             stat = client.checkExists().forPath(path);
        } catch (Exception e) {
            log.error("获取节点信息失败,失败原因：" + e);
            throw new RuntimeException("Failed to get node information!");
        }
        if(stat != null){
            log.error("节点已存在");
            throw new RuntimeException("Node already exists!");
        }
    }

    @Override
    public void checkNodeNotExists(String path){
        Stat stat = null;
        try {
            stat = client.checkExists().forPath(path);
        } catch (Exception e) {
            log.error("获取节点信息失败,失败原因：" + e);
            throw new RuntimeException("Failed to get node information!");
        }
        if(stat == null){
            log.error("节点不存在");
            throw new RuntimeException("Node not exists!");
        }
    }

    @Override
    public List<String> queryChildPathList(String path) {
        checkNodeNotExists(path);

        try {
            return client.getChildren().forPath(path);
        } catch (Exception e) {
            log.error("获取子列表失败,失败原因：" + e);
            throw new RuntimeException("Failed to get child node list!");
        }
    }

    @Override
    public String queryAllPath(String path) {
        checkNodeNotExists(path);

        if(!"/".equals(path)){
            localPath.get().add(path);
        }

        try {
            List<String> list =  client.getChildren().forPath(path);
            for(String child : list){
                if("/".equals(path)){
                    path = "";
                }
                queryAllPath(path + "/" + child);
            }
        } catch (Exception e) {
            log.error("获取子列表失败,失败原因：" + e);
            throw new RuntimeException("Failed to get child node list!");
        }
        return path;
    }

    @Override
    public PageInfo<Property> convertMapToModel(Map<String, String> map,Map<String, String> map1) {

        Integer currentPage = Integer.valueOf(map1.get("page"));
        Integer pageSize = Integer.valueOf(map1.get("limit"));

        PageHelper.startPage(currentPage, pageSize);

        List<Property> list = new ArrayList<>();

        for(Map.Entry<String,String> entry : map.entrySet()){
            Property p = new Property();
            p.setPath(entry.getKey());
            p.setData(entry.getValue());
            list.add(p);
        }
        return new PageInfo<Property>(list);
    }

}
